/*
 * =====================================================================================
 *
 *       Filename:  MySerial.cpp
 *
 *    Description:  this is a serial class of mine
 *
 *        Version:  1.0
 *        Created:  2012年04月09日 10时32分02秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Michael lee
 *        Company:
 *
 * =====================================================================================
 */

#include "MySerial.h"
MySerial::MySerial()
{
    mFd=-1;
    memset(mReadBuf,0,sizeof(mReadBuf));
    memset(mWriteBuf,0,sizeof(mWriteBuf));
}

MySerial::~MySerial()
{

}

//open serial port
//return fd
int MySerial::openPort(int comport)
{
    char *dev[]= {"/dev/ttyS0","/dev/ttyS1","/dev/ttyS2","/dev/ttyS3","/dev/ttyS4","/dev/ttyS5"};
    switch (comport)
    {
        case 0:
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
        {
            mFd=open(dev[comport],O_RDWR|O_NOCTTY|O_NDELAY);
            if (-1==mFd)
            {
                perror("Can't open serial port");
                return -1;
            }
            break;
        }
        default:
            break;
    }

    //恢复串口为阻塞状态
    if (fcntl(mFd,F_SETFL,0)<0)
    {
        printf("fcntl failed!\n");
    }
    else printf("fcntl=%d\n",fcntl(mFd,F_SETFL,0));

    //测试是否为终端设备
    if (0==isatty(STDIN_FILENO))
    {
        printf("stand input is not a terminal device\n");
    }
    else
        printf("isatty success!\n");
    return mFd;
}

//set serial option  //setOpt(115200,8,'N',1)
int MySerial::setOpt(int nSpeed, int nBits, char cEvent,int nStop) const
{
    struct termios newTio,oldTio;
    //保存测试现有参数设置 如果串口号等出错 这里会报错
    if (tcgetattr(mFd, &oldTio) != 0)
    {
        perror("Setup Serial");
        return -1;
    }
    bzero(&newTio, sizeof(newTio));
    //设置字符大小
    newTio.c_cflag |= CLOCAL | CREAD;
    newTio.c_cflag &=~CSIZE;

    //设置数据位
    switch (nBits)
    {
        case 7:
            newTio.c_cflag|=CS7;
            break;
        case 8:
            newTio.c_cflag|=CS8;
            break;
        default:
            printf("unknow databit\n");
            break;
    }

    //奇偶校验
    switch (cEvent)
    {
        case 'N':                  /* no parity check */
            newTio.c_cflag &= ~PARENB;
            break;
        case 'E':                  /* even */
            newTio.c_cflag |= PARENB;
            newTio.c_cflag &= ~PARODD;
            break;
        case 'O':                  /* odd */
            newTio.c_cflag |= (PARODD | PARENB);
            break;
        default:                   /* no parity check */
            newTio.c_cflag &= ~PARENB;
            break;
    }

    //baud
    switch (nSpeed)
    {
        case 2400:
            cfsetispeed(&newTio,B2400);
            cfsetospeed(&newTio,B2400);
            break;
        case 4800:
            cfsetispeed(&newTio,B4800);
            cfsetospeed(&newTio,B4800);
            break;
        case 9600:
            cfsetispeed(&newTio,B9600);
            cfsetospeed(&newTio,B9600);
            break;
        case 115200:
            cfsetispeed(&newTio,B115200);
            cfsetospeed(&newTio,B115200);
            break;
        case 460800:
            cfsetispeed(&newTio,B460800);
            cfsetospeed(&newTio,B460800);
            break;
        default:
            cfsetispeed(&newTio,B9600);
            cfsetospeed(&newTio,B9600);
            break;
    }

    //设置停止位
    if (1==nStop)
    {
        newTio.c_cflag &= ~CSTOPB;
    }
    else if (2==nStop)
    {
        newTio.c_cflag |=CSTOPB;
    }

    //设置等待时间和最小接收字符
    newTio.c_cc[VTIME]=0;
    newTio.c_cc[VMIN]=0;
    //处理未接受字符
    tcflush(mFd,TCIFLUSH);
    //激活新配置
    if ((tcsetattr(mFd,TCSANOW,&newTio))!=0)
    {
        perror("com set error");
        return -1;
    }
    return 0;
}

//write data, return the len of data have write;
int MySerial::writeData(const unsigned char *data, int writeLen)
{
    if (mFd>-1)
    {
        return write(mFd,data,writeLen);
    }
    else
    {
        printf("write:fd<=-1\n");
        return -1;
    }

}

//read data
int MySerial::readData(unsigned char *buf, int readLen, int nTimeOutMs)
{
    if (mFd>-1)
    {
        //return read(mFd,buf,readLen);
        int nRead=0;
        FD_ZERO(&mRd);
        FD_SET(mFd,&mRd);
        struct timeval tvOut;
        tvOut.tv_sec = nTimeOutMs/1000;
        tvOut.tv_usec = (nTimeOutMs%1000)*1000;
        if (FD_ISSET(mFd,&mRd))
        {
            if (0==nTimeOutMs)
            {
                switch (select(mFd+1,&mRd,NULL,NULL,NULL))
                {
                    case -1:
                        printf("select is error\n");
                        break;
                    case 0: //timeout
                        break;
                    default:
                        nRead = read(mFd, buf, readLen);
                        break;
                }
            }
            else
            {
                switch (select(mFd+1,&mRd,NULL,NULL,&tvOut))
                {
                    case -1:
                        printf("select is error\n");
                        break;
                    case 0: //timeout
                        break;
                    default:
                        nRead = read(mFd, buf, readLen);
                        break;
                }
            }

        }
        return nRead;
    }
    else
    {
        printf("read:fd<=-1\n");
        return -1;
    }
}

//close
void MySerial::closePort()
{
    close(mFd);
    mFd=-1;
    memset(mReadBuf,0,sizeof(mReadBuf));
    memset(mWriteBuf,0,sizeof(mWriteBuf));
}

